/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <string>
#include <memory>
#include <baja/db/db_error.h>
#include <baja/db/db_interfaces.h>
#include <baja/db/db_cell_typed.hpp>

namespace baja { namespace storage {

const unsigned int UnicodeCodePage = 1200;
const unsigned int ANSICodePage = 1252;
const unsigned int CurrentCodePage = UnicodeCodePage;
const unsigned int MaxStringSizeInBytes = 260 * sizeof(wchar_t);
const unsigned int MaxBinaryBlobSize = 1048576;

template<class T>
class column_typed : public icolumn_typed<T>, public std::enable_shared_from_this<column_typed<T>>
{
public:
    column_typed(const std::wstring& name, const column_type& type) :
        _id(0),
        _name(name),
        _type(type),
        _maxBufferSizeInBytes(0),
        _fixed(true),
        _autoIncrement(false),
        _isBlobSize(false),
        _appendOnUpdate(false)
    {
    }
    static std::shared_ptr<icolumn_typed<T>> create(std::wstring name, column_type type)
    {
        std::shared_ptr<column_typed<T>> returnValue = std::make_shared<column_typed<T>>(name, type);;
        return returnValue;
    }

    // icolumn
    void setId(column_id id)
    {
        _id = id;
    }

    void setFixed(bool fixed)
    {
        _fixed = fixed;
    }
    
    void setAutoIncrement(bool autoIncrement)
    {
        _autoIncrement = autoIncrement;
    }

    void setMaxBufferSizeInBytes(b::uint32 sizeInBytes)
    {
        BAJA_THROW(std::logic_error("column_typed::setMaxBufferSizeInBytes() is not supported for this type."));
    }

    void setIsBlobSize(bool isBlobSize)
    {
        _isBlobSize = isBlobSize;
    }

    column_id getId()
    {
        return _id;
    }

    std::wstring getName()
    {
        return _name;
    }

    column_type getType()
    {
        return _type;
    }

    bool getFixed()
    {
        return _fixed;
    }

    bool getAutoIncrement()
    {
        return _autoIncrement;
    }

    bool getIsBlobSize()
    {
        return _isBlobSize;
    }

    unsigned int getCodePage()
    {
        return CurrentCodePage;
    }

    unsigned long getMaxBufferSizeInBytes()
    {
        BAJA_THROW(std::logic_error("column_typed::getMaxBufferSizeInBytes() is not supported for this type."));
        return 0;
    }

    // icolumn_typed
    std::shared_ptr<icell_typed<T>> createCell(T value)
    {
        return baja::storage::createCell<T>(shared_from_this(), value);
    }
    std::shared_ptr<icell_typed<T>> createCellByMoving(T& value)
    {
        return baja::storage::createCellByMoving<T>(shared_from_this(), value);
    }

protected:
    column_id _id;
    std::wstring _name;
    column_type _type;
    unsigned long _maxBufferSizeInBytes;
    bool _fixed;
    bool _autoIncrement;
    bool _isBlobSize;
    bool _appendOnUpdate;
};

class string_column : public column_typed<std::wstring>
{
public:
    string_column(const std::wstring& name, const column_type& type) :
        column_typed<std::wstring>(name, type)
    {
        _maxBufferSizeInBytes = MaxStringSizeInBytes;
    }
    static std::shared_ptr<string_column> create(std::wstring name, column_type type)
    {
        std::shared_ptr<string_column> returnValue = std::make_shared<string_column>(name, type);;
        return returnValue;
    }
    void setMaxBufferSizeInBytes(b::uint32 sizeInBytes)
    {
        _maxBufferSizeInBytes = sizeInBytes;
    }
    unsigned long getMaxBufferSizeInBytes()
    {
        return _maxBufferSizeInBytes;
    }
};

class blob_column : public column_typed<binary_blob>
{
public:
    blob_column(const std::wstring& name, const column_type& type) :
        column_typed<binary_blob>(name, type)
    {
        _maxBufferSizeInBytes = MaxBinaryBlobSize;
    }
    static std::shared_ptr<blob_column> create(std::wstring name, column_type type)
    {
        std::shared_ptr<blob_column> returnValue = std::make_shared<blob_column>(name, type);;
        return returnValue;
    }
    void setMaxBufferSizeInBytes(b::uint32 sizeInBytes)
    {
        _maxBufferSizeInBytes = sizeInBytes;
    }
    unsigned long getMaxBufferSizeInBytes()
    {
        return _maxBufferSizeInBytes;
    }
};


inline std::shared_ptr<icolumn_typed<std::wstring>> createStringColumn(std::wstring name, column_type type)
{
    return string_column::create(name, type);
}

inline std::shared_ptr<icolumn_typed<binary_blob>> createBlobColumn(std::wstring name, column_type type)
{
    return blob_column::create(name, type);
}

template<class T>
std::shared_ptr<icolumn_typed<T>> createColumn(std::wstring name, column_type type)
{
    return column_typed<T>::create(name, type);
}

}} // namespace baja::storage